package at.ltasat.train.validation;

import java.io.ByteArrayInputStream;
import java.io.PrintStream;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import at.ltasat.ArgumentClazzData;
import at.ltasat.ArgumentClazzDataClazzManagerIO;
import at.ltasat.CnfClassifierFactories;
import at.ltasat.CnfFeaturesFactory;
import at.ltasat.classifier.Classification;
import at.ltasat.classifier.Classifier;
import at.ltasat.classifier.Clazz;
import at.ltasat.classifier.Membership;
import at.ltasat.classifier.io.ClassifierIO;
import at.ltasat.train.set.DimacsFuzzer;
import at.ltasat.train.set.ManualClassifierFactory;
import at.ltasat.util.Average;
import at.tasat.cnf.Cnf;
import at.tasat.io.OutputFormatter;
import at.tasat.parser.DimacsParser;

public final class ValidateClassifierMain {

	public static void main(String[] args) {
		ValidateClassifierConfiguration config = ValidateClassifierConfiguration.get(args);
		int result = main(config);
		System.exit(result);
	}

	public static int main(ValidateClassifierConfiguration config) {
		final boolean verbose = config.isVerboseStatus();
		final PrintStream out = new PrintStream(config.getOut());
		try {
			if (config.isVerboseConfig()) {
				out.println(OutputFormatter.getCommentString(config.toString()));
			}
			log(verbose, out, "starting validation");
			final Classifier<Cnf, ArgumentClazzData> classifier = ClassifierIO
					.read(config.getIn(), CnfClassifierFactories.INSTANCE, new ArgumentClazzDataClazzManagerIO(), CnfFeaturesFactory.INSTANCE);
			final Classifier<byte[], ArgumentClazzData> reference = (new ManualClassifierFactory(config.getIterations(), config.getMaxTime())).createTrainer(classifier.getClazzManager(), null)
					.create();
			final Random random = new Random();
			final List<Integer> positions = new LinkedList<Integer>();
			int hits = 0;
			for (int count = 0; count < config.getSize(); count++) {
				final int nVariables = random.nextInt(config.getVariables()) + 1;
				final int nClauses = random.nextInt(config.getClauses()) + 1;
				log(verbose, out, "data #%d (%d,%d)", count, nVariables, nClauses);
				final byte[] cnf = DimacsFuzzer.getRandomCnf(nVariables, nClauses).getBytes();
				final Clazz<ArgumentClazzData> expectedClazz = reference.classify(cnf).getDominantClazz();
				final Classification<?, ArgumentClazzData> classification = classifier.classify(DimacsParser.parse(new ByteArrayInputStream(cnf)));
				if (classification.getDominantClazz() == expectedClazz) {
					hits++;
				}
				for (int i = 0; i < classification.getNumberOfMemberships(); i++) {
					final Membership<ArgumentClazzData> memberships = classification.getMembership(i);
					if (memberships.getClazz() == expectedClazz) {
						log(verbose, out, "position: %d", i);
						positions.add(i);
						break;
					}
				}
			}
			Average result = Average.get(positions);
			out.println(OutputFormatter.getCommentString(String.format("hits: %f", ((double) hits) / config.getSize())));
			out.println(OutputFormatter.getCommentString(String.format("average position: %f", result.average())));
			out.println(OutputFormatter.getCommentString(String.format("standard deviation position: %f", result.deviation())));
			return 0;
		} catch (Throwable error) {
			(new PrintStream(config.getErr())).println(OutputFormatter.getErrorString(error));
			return 1;
		}
	}

	private static void log(boolean verbose, PrintStream out, String msg, Object... args) {
		if (verbose) {
			out.println(OutputFormatter.getCommentString(String.format(msg, args)));
		}
	}

}
